/*!
    \copyright  Copyright (c) 2022-2023 Qualcomm Technologies International, Ltd.
                All Rights Reserved.
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \version    
    \file       google_finder.c
    \ingroup    google_finder
    \brief      Component handling Google Finder Core functionality.
*/

#ifdef INCLUDE_GOOGLE_FINDER
#include <panic.h>
#include <vm.h>
#include <string.h>
#include <stdio.h>
#include <logging.h>
#include <gatt_fast_pair_server.h>
#include "google_finder.h"
#include "fast_pair_gfps.h"
#include "google_finder_private.h"
#include "gatt_connect.h"
#include "cryptoalgo.h"
#include "user_accounts.h"
#include "bt_device.h"
#include "tx_power.h"
#include "multidevice.h"
#include "google_finder_advertiser.h"
#include "google_finder_pddu.h"
#include "device_properties.h"
#include "device_db_serialiser.h"
#include "ui.h"
#include "fast_pair_msg_stream_dev_action.h"
#include "fast_pair.h"
#include "power_manager.h"
#include "pddu_map.h"

#ifdef FIND_MY_DEVICE_ENABLE_DULT
#include "dult.h"
#endif

const message_group_t google_finder_ui_inputs[] =
{
    UI_INPUTS_HANDSET_MESSAGE_GROUP
};

/* Google Finder Task Data */
googleFinderTaskData google_finder_task_data;

extern google_finder_advert_data_t google_finder_advert;
extern uint8 google_finder_adv_packet[GOOGLE_FINDER_ADV_P160_DATA_PACKET_SIZE];

/* Callback functions to handle GATT Connect/Disconnect notifications */
static void googleFinder_GattConnect(gatt_cid_t cid);
static void googleFinder_GattDisconnect(gatt_cid_t cid);

static const gatt_connect_observer_callback_t google_finder_gatt_connect_callback =
{
    .OnConnection = googleFinder_GattConnect,
    .OnDisconnection = googleFinder_GattDisconnect
};

/*! \brief Get pointer to Google Finder task data structure.
    \param void
    \return googleFinderTaskData pointer to Google Finder task data structure
*/
googleFinderTaskData *googleFinder_GetTaskData(void)
{
    return (&google_finder_task_data);
}

static google_finder_instance* googleFinder_GetConnectionInstance(uint32 bt_conn_id)
{
    uint8 index;
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();

    for(index = 0; index < MAX_BLE_CONNECTIONS; index++)
    {
        if(google_finder_task_ptr->connection_instance[index].conn_id == bt_conn_id)
        {
            return &google_finder_task_ptr->connection_instance[index];
        }
    }

    return NULL;
}

/* Helper function to Create or Get connection data for a given conn id */
static google_finder_instance* googleFinder_CreateOrGetConnectionInstance(uint32 bt_conn_id)
{
    uint8 index;
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();
    google_finder_instance *conn_instance;

    if((conn_instance = googleFinder_GetConnectionInstance(bt_conn_id)) != NULL)
    {
        return conn_instance;
    }

    /* Create a new connection data instance */
    for(index = 0; index < MAX_BLE_CONNECTIONS; index++)
    {
        if(google_finder_task_ptr->connection_instance[index].conn_id == GOOGLE_FINDER_INVALID_CID)
        {
            google_finder_task_ptr->connection_instance[index].conn_id = bt_conn_id;

            DEBUG_LOG("googleFinder_CreateOrGetConnectionData. New instance created.");

            return &google_finder_task_ptr->connection_instance[index];
        }
    }

    DEBUG_LOG("googleFinder_CreateOrGetConnectionData. Shouldn't have reached here.");
    Panic();

    return NULL;
}

/* Reset Google Finder connection data for a given connection id */
static void googleFinder_ResetConnectionInstance(uint32 bt_conn_id)
{
    google_finder_instance *instance;

    if((instance = googleFinder_GetConnectionInstance(bt_conn_id)) != NULL)
    {
        free(instance->random_nonce);
        instance->random_nonce = NULL;
        free(instance->account_key);
        instance->account_key = NULL;
        instance->conn_id = GOOGLE_FINDER_INVALID_CID;

        DEBUG_LOG("googleFinder_ResetConnectionData.connection data got reset for conn id: 0x%x", bt_conn_id);
    }
}

/* Reset nonce for a given connection id */
static void googleFinder_ResetNonceForConnection(uint32 bt_conn_id)
{
    google_finder_instance *instance;

    if((instance = googleFinder_GetConnectionInstance(bt_conn_id)) != NULL)
    {
        free(instance->random_nonce);
        instance->random_nonce = NULL;

        DEBUG_LOG("googleFinder_ResetNonceForConnection. nonce data got reset for conn id: 0x%x", bt_conn_id);
    }
}

/* Reset the ring operation BT Connection ID for the given connection id */
static void googleFinder_ResetRingOperationConnId(uint32 bt_conn_id)
{
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();

    if(google_finder_task_ptr->ring_data.instance != NULL && 
       google_finder_task_ptr->ring_data.instance->conn_id == bt_conn_id)
    {
        DEBUG_LOG("googleFinder_ResetRingOperationConnId");
        google_finder_task_ptr->ring_data.instance = NULL;

        /* After the disconnection, device need not send ring response when ringing timesout,
         * so no need of storing the ring nonce
         */
        if (google_finder_task_ptr->ring_data.ring_nonce != NULL)
        {
            free(google_finder_task_ptr->ring_data.ring_nonce);
            google_finder_task_ptr->ring_data.ring_nonce = NULL;
        }
    }
}

static void googleFinder_HandleReadBeaconActionInd(GATT_FAST_PAIR_SERVER_READ_BEACON_ACTION_IND_T *read_ind)
{
    uint8* resp_data = NULL;
    google_finder_instance* instance = googleFinder_CreateOrGetConnectionInstance(read_ind->btConnId);

    if(instance->random_nonce == NULL)
    {
        instance->random_nonce = FastPair_GenerateRandomNonce();
    }

    DEBUG_LOG("googleFinder_HandleReadBeaconActionInd, btConnId=0x%x", read_ind->btConnId);

    resp_data = (uint8*)PanicUnlessMalloc(GOOGLE_FINDER_READ_RSP_LEN);

    resp_data[GOOGLE_FINDER_READ_RSP_VERSION_OFFSET] = GOOGLE_FINDER_SUPPORTED_VERSION;
    memcpy(&resp_data[GOOGLE_FINDER_READ_RSP_NONCE_OFFSET], instance->random_nonce, GOOGLE_FINDER_ONE_TIME_NONCE_LEN);

    DEBUG_LOG("googleFinder_HandleReadBeaconActionInd, read resp data :");
    DEBUG_LOG_DATA_DEBUG(resp_data, GOOGLE_FINDER_READ_RSP_LEN);

    FastPair_BeaconActionReadResponse(read_ind->fast_pair_server,
                                      read_ind->btConnId,
                                      resp_data,
                                      GOOGLE_FINDER_READ_RSP_LEN);
}

/*! \brief Get owner account key associated for beacon provisioning
    \param None
    \return Pointer to the 16 octets owner account key, should be freed by the caller.
*/
static inline uint8 *googleFinder_GetOwnerAccountKey(void)
{
    return fastPair_GetOwnerAccountKey();
}

/*! \brief Function to check if EIK is zero
    \param None
    \return TRUE if EIK is zero, FALSE otherwise.
*/
static bool googleFinder_IsEIKZero(void)
{
    device_t my_device = BtDevice_GetSelfDevice();
    uint8 cleared_eik[GOOGLE_FINDER_EIK_LEN];

    memset(cleared_eik, 0x00, GOOGLE_FINDER_EIK_LEN);

    if(my_device != NULL)
    {
        google_finder_pddu_t *google_finder_pddu = NULL;
        size_t google_finder_pddu_size = 0;

        if(Device_GetProperty(my_device, device_property_google_finder, (void *)&google_finder_pddu, &google_finder_pddu_size))
        {
            if(memcmp(google_finder_pddu->eik, cleared_eik, GOOGLE_FINDER_EIK_LEN) != 0)
            {
                return FALSE;
            }
        }
    }

    return TRUE;
}

/*! \brief Get the Ephemeral Identity Key stored by the Provider
    \param None
    \return Pointer to 32 byte EIK, should be freed by the caller.
*/
uint8 *googleFinder_GetEIK(void)
{
    google_finder_pddu_t *google_finder_pddu = NULL;
    size_t google_finder_pddu_size = 0;
    device_t my_device = BtDevice_GetSelfDevice();

    if(googleFinder_IsEIKZero())
    {
        return NULL;
    }

    if(BtDevice_GetDeviceType(my_device) == DEVICE_TYPE_SELF)
    {
        if(Device_GetProperty(my_device, device_property_google_finder, (void *)&google_finder_pddu, &google_finder_pddu_size))
        {
            uint8* eik = (uint8*)PanicUnlessMalloc(GOOGLE_FINDER_EIK_LEN);
            memcpy(eik, google_finder_pddu->eik, GOOGLE_FINDER_EIK_LEN);

            return eik;
        }
    }

    return NULL;
}

/* Helper function to generate different type of secondary key, 
 * its caller's responsibility to free returned non-NULL key 
 */
static uint8 *googleFinder_GenerateSecondaryKey(finder_key_type_t key_type)
{
    uint8 *eik;
    uint8 sha256_ip[GOOGLE_FINDER_EIK_LEN + GOOGLE_FINDER_KEY_TYPE_LEN];
    uint8 sha256_out[SHA256_DIGEST_SIZE];

    if((eik = googleFinder_GetEIK()) != NULL)
    {
        uint8 *secondary_key = (uint8*)PanicUnlessMalloc(GOOGLE_FINDER_SECONDARY_KEY_LEN);

        /* First 8 bytes of SHA256(Ephemeral Identity Key || key_type) */
        memcpy(&sha256_ip[0], eik, GOOGLE_FINDER_EIK_LEN);
        sha256_ip[GOOGLE_FINDER_EIK_LEN] = key_type;

        sha256(sha256_ip, GOOGLE_FINDER_EIK_LEN + GOOGLE_FINDER_KEY_TYPE_LEN, sha256_out);

        memcpy(secondary_key, sha256_out, GOOGLE_FINDER_SECONDARY_KEY_LEN);

        DEBUG_LOG("googleFinder_GenerateSecondaryKey key_type : 0x%x", key_type);
        free(eik);

        return secondary_key;
    }

    DEBUG_LOG("googleFinder_GenerateSecondaryKey failed to generate key_type : 0x%x", key_type);
    return NULL;
}

/* Helper function to generate recovery key, its caller's responsibility to free returned non-NULL key */
static uint8 *googleFinder_GetRecoveryKey(void)
{
    return googleFinder_GenerateSecondaryKey(GOOGLE_FINDER_RECOVERY_KEY);
}

/* Helper function to generate ring key, its caller's responsibility to free returned non-NULL key */
static uint8 *googleFinder_GetRingKey(void)
{
    return googleFinder_GenerateSecondaryKey(GOOGLE_FINDER_RING_KEY);
}

/* Helper function to generate unwanted tracking protection key, 
 * its caller's responsibility to free returned non-NULL key 
 */
static uint8 *googleFinder_GetUnwantedTrackingKey(void)
{
    return googleFinder_GenerateSecondaryKey(GOOGLE_FINDER_UNWANTED_TRACKING_KEY);
}

/*! \brief Get account key for incoming connection identifier.
           Caller of the function should free the memory allocated for account key
    \param btConnId connection identifier
    \return Pointer to the account key.
*/
static uint8* googleFinder_GetAccountKeyForConnId(uint32 btConnId)
{
    tp_bdaddr tpaddr = {0};
    uint8 *account_key = NULL;
    google_finder_instance *instance = googleFinder_GetConnectionInstance(btConnId);

    DEBUG_LOG("googleFinder_GetAccountKeyForConnId: btConnId=0x%x", btConnId);

    if (instance != NULL)
    {
        /* Return the copy of cached account key, this might have been updated 
         * based on matched OTA key, even if BLE pairing has not been done 
         * to resolve the RPA to identify account key.
         */
        if (instance->account_key != NULL)
        {
            account_key = (uint8*)PanicUnlessMalloc(GOOGLE_FINDER_ACCOUNT_KEY_LEN);
            memcpy(account_key, instance->account_key, GOOGLE_FINDER_ACCOUNT_KEY_LEN);

            return account_key;
        }
    }

    if(VmGetBdAddrtFromCid(gattConnect_GetCid(btConnId), &tpaddr))
    {
        account_key = UserAccounts_GetAccountKeyWithHandset(GOOGLE_FINDER_ACCOUNT_KEY_LEN, &tpaddr);

        /* Cache the account key for the instance, will be freed on GATT disconnection. */
        if(instance != NULL && instance->account_key == NULL && account_key != NULL)
        {
            instance->account_key = (uint8*)PanicUnlessMalloc(GOOGLE_FINDER_ACCOUNT_KEY_LEN);
            memcpy(instance->account_key, account_key, GOOGLE_FINDER_ACCOUNT_KEY_LEN);
        }
    }

    return account_key;
}

static uint8* googleFinder_ValidateOTAWithAnyAccountKey(uint8 *data, uint8 data_size,
                                                 uint8 *hmac_sha256_out,
                                                 uint8 *expected_hmac_sha256_out)
{
    uint8 *keys = NULL;
    uint8 *account_key = NULL;
    uint8 num_account_keys = UserAccounts_GetAccountKeys(&keys, user_account_type_fast_pair);
    uint8 index;

    DEBUG_LOG("googleFinder_ValidateOTAWithAnyAccountKey : Stored account keys %d", num_account_keys);

    for (index = 0; index < num_account_keys; index++)
    {
        hmac_sha256(data, data_size, hmac_sha256_out, &keys[index * GOOGLE_FINDER_ACCOUNT_KEY_LEN], GOOGLE_FINDER_ACCOUNT_KEY_LEN);

        if(memcmp(expected_hmac_sha256_out, hmac_sha256_out, GOOGLE_FINDER_ONE_TIME_AUTH_KEY_LEN) == 0)
        {
            /* Matched OTA with the current account key */
            account_key = (uint8*)PanicUnlessMalloc(GOOGLE_FINDER_ACCOUNT_KEY_LEN);
            memcpy(account_key, &keys[index * GOOGLE_FINDER_ACCOUNT_KEY_LEN], GOOGLE_FINDER_ACCOUNT_KEY_LEN);
            break;
        }
    }

    free(keys);

    return account_key;
}


/*! \brief Generate the authentication data for given data ID.

    \param instance pointer to google finder connection instance 
    \param auth_data_ip pointer to input parameters for auth data calculation  

    \return Pointer to the authentication data (To be freed by the caller of the function).
*/
static uint8 *googleFinder_GenerateAuthenticationData(google_finder_instance *instance,
                                                      google_finder_auth_data_input *auth_data_ip)
{
    uint8 *data;
    uint8 *key = NULL;
    uint16 key_size = 0;
    uint8 data_size;
    uint8 *hmac_sha256_out;
    uint8 *random_nonce = NULL;
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();

    DEBUG_LOG("googleFinder_GenerateAuthenticationData. Data ID : 0x%x", auth_data_ip->data_id);

    /* For ring state events, use nonce from ring request data for other responses 
     * and requests nonce shall be used from respective connection instance.
     */
    if(auth_data_ip->is_rsp && auth_data_ip->data_id == GOOGLE_FINDER_RING_DEVICE && 
       google_finder_task_ptr->ring_data.ring_nonce != NULL)
    {
        random_nonce = google_finder_task_ptr->ring_data.ring_nonce;
    }
    else
    {
        random_nonce = instance->random_nonce;
    }

    /* Cannot generate authentication data without nonce */
    if (random_nonce == NULL)
    {
        return NULL;
    }

    /* Service response data format - (Protocol major version number || the last nonce read from the characteristic || Data ID || Data length || Additional Data || 0x01). */
    if(auth_data_ip->is_rsp)
    {
        data_size = GOOGLE_FINDER_BEACON_OTA_FIXED_DATA_LEN + auth_data_ip->add_data_len + 1;
    }
    /* OTA data format - (Protocol major version number || the last nonce read from the characteristic || Data ID || Data length || Additional Data) */
    else
    {
        data_size = GOOGLE_FINDER_BEACON_OTA_FIXED_DATA_LEN + auth_data_ip->add_data_len;
    }

    data = (uint8*)PanicUnlessMalloc(data_size);

    data[GOOGLE_FINDER_OTA_MAJ_VERSION_OFFSET] = GOOGLE_FINDER_SUPPORTED_VERSION;
    memcpy(data+GOOGLE_FINDER_OTA_NONCE_OFFSET, random_nonce, GOOGLE_FINDER_ONE_TIME_NONCE_LEN);
    data[GOOGLE_FINDER_OTA_DATA_ID_OFFSET] = auth_data_ip->data_id;
    data[GOOGLE_FINDER_OTA_DATA_LENGTH_OFFSET] = auth_data_ip->data_len;

    if(auth_data_ip->add_data != NULL)
    {
        memcpy(&data[GOOGLE_FINDER_OTA_ADD_DATA_OFFSET], auth_data_ip->add_data, auth_data_ip->add_data_len);
    }

    if(auth_data_ip->is_rsp)
    {
        data[GOOGLE_FINDER_BEACON_OTA_FIXED_DATA_LEN + auth_data_ip->add_data_len] = GOOGLE_FINDER_BEACON_RSP_PADDING;
    }

    hmac_sha256_out = (uint8*)PanicUnlessMalloc(SHA256_DIGEST_SIZE);

    switch(auth_data_ip->data_id)
    {
        /* Key to be used for these operations is account key, go through all account keys */
        case GOOGLE_FINDER_READ_BEACON_PARAMS:
        case GOOGLE_FINDER_READ_PROVISION_STATE:
        {
            uint8 *account_key;
            key = googleFinder_GetAccountKeyForConnId(instance->conn_id);
            key_size = GOOGLE_FINDER_ACCOUNT_KEY_LEN;

            /* If BLE pairing has not been done with the remote device, its not 
             * possible to get account key from RPA address, in such cases iterate
             * through all available account keys to match the authentication data.
             */
            if (key == NULL && !auth_data_ip->is_rsp && 
                (account_key = googleFinder_ValidateOTAWithAnyAccountKey(data, data_size, 
                                                    hmac_sha256_out, auth_data_ip->ota_key)) != NULL)
            {
                /* Cache the account key, so that for next operation googleFinder_GetAccountKeyForConnId() 
                 * can return the correct account key without going through all the account keys to match
                 */
                instance->account_key = account_key;
                free(data);
                DEBUG_LOG("googleFinder_GenerateAuthenticationData. OTA verified with one of the saved account key");
                return hmac_sha256_out;
            }
        }
        break;

        /* Key to be used for these operations is owner account key */
        case GOOGLE_FINDER_SET_EIK:
        case GOOGLE_FINDER_CLEAR_EIK:
        {
            key = googleFinder_GetOwnerAccountKey();
            key_size = GOOGLE_FINDER_ACCOUNT_KEY_LEN;
        }
        break;

        /* Key to be used for this operation is recovery key */
        case GOOGLE_FINDER_READ_EIK_USER_CONSENT:
        {
            key = googleFinder_GetRecoveryKey();
            key_size = GOOGLE_FINDER_RECOVERY_KEY_LEN;
        }
        break;

        /* Key to be used for these operations is ring key */
        case GOOGLE_FINDER_RING_DEVICE:
        case GOOGLE_FINDER_READ_RING_STATE:
        {
            key = googleFinder_GetRingKey();
            key_size = GOOGLE_FINDER_RING_KEY_LEN;;
        }
        break;

        /* Key to be used for these operations is unwanted tracking protection key */
        case GOOGLE_FINDER_ACTIVATE_UNWANTED_TRACKING:
        case GOOGLE_FINDER_DEACTIVATE_UNWANTED_TRACKING:
        {
            key = googleFinder_GetUnwantedTrackingKey();
            key_size = GOOGLE_FINDER_UNWANTED_TRACKING_KEY_LEN;
        }
        break;

        default:
        {
            DEBUG_LOG("googleFinder_GenerateAuthenticationData: Unsupported Beacon Action Message.");
        }
        break;
    }

    if(key != NULL)
    {
        hmac_sha256(data, data_size, hmac_sha256_out, key, key_size);
        free(key);
        free(data);
    }
    else
    {
        DEBUG_LOG("googleFinder_GenerateAuthenticationData. Key used for OTA verification is NULL or invalid");
        free(data);
        free(hmac_sha256_out);
        return NULL;
    }

    return hmac_sha256_out;
}

/*! \brief Verify One Time Authentication Key present in the write request packet.

    \param instance pointer to google finder connection instance 
    \param auth_data_ip pointer to input parameters for auth data calculation  

    \return TRUE if Verification of OTA Key succeeds, FALSE otherwise
*/
static bool googleFinder_VerifyOTAKey(google_finder_instance* instance,
                                      google_finder_auth_data_input *auth_data_ip)
{
    bool status = FALSE;
    uint8 *auth_data = NULL;

    DEBUG_LOG("googleFinder_VerifyOTAKey. Data ID : 0x%x", auth_data_ip->data_id);

    /* Generate Authentication Data and validate it againt OTA key.*/
    auth_data = googleFinder_GenerateAuthenticationData(instance, auth_data_ip);

    if(auth_data != NULL)
    {
        /* If first 8 octets of hmac_sha256 matches with the OTA key, further process the write request*/
        if(memcmp(auth_data_ip->ota_key, auth_data, GOOGLE_FINDER_ONE_TIME_AUTH_KEY_LEN) == 0)
        {
            DEBUG_LOG("googleFinder_VerifyOTAKey. OTA Key is matching with the first 8 octets of HMAC SHA256 output.");
            status = TRUE;
        }
        else
        {
            DEBUG_LOG("googleFinder_VerifyOTAKey. OTA Key is not matching.");
        }
        free(auth_data);
    }
    else
    {
        DEBUG_LOG("googleFinder_VerifyOTAKey. Generated authentication data is not valid.");
    }

    return status;
}

/*! \brief Function to send Beacon Action Service Response.

    \param btConnId connection identifier of write request packet
    \param data_id data id of beacon action message
    \param add_data additional data to be included in beacon action service response
    \param add_data_len additional data length
    \return void

    \note Caller of this function is responsible to free the additional data.
*/
static void googleFinder_BeaconActionServiceResponse(google_finder_instance* instance,
                                                     uint8 data_id,
                                                     uint8* add_data,
                                                     uint8 add_data_len)
{
    uint8* rsp_data = NULL;
    uint8* auth_data = NULL;
    google_finder_auth_data_input auth_data_ip;
    uint8  rsp_len = GOOGLE_FINDER_BEACON_RSP_DATA_ID_LEN +
                     GOOGLE_FINDER_BEACON_RSP_DATA_LEN +
                     GOOGLE_FINDER_BEACON_RSP_AUTH_LEN +
                     add_data_len;

    DEBUG_LOG("googleFinder_BeaconActionServiceResponse. Data ID: %02x", data_id);

    rsp_data = (uint8*)PanicUnlessMalloc(rsp_len);

    rsp_data[GOOGLE_FINDER_BEACON_RSP_DATA_ID_OFFSET] = data_id;
    rsp_data[GOOGLE_FINDER_BEACON_RSP_DATA_LEN_OFFSET] = GOOGLE_FINDER_BEACON_RSP_AUTH_LEN + add_data_len;

    auth_data_ip.data_id = data_id;
    auth_data_ip.data_len = rsp_data[GOOGLE_FINDER_BEACON_RSP_DATA_LEN_OFFSET];
    auth_data_ip.add_data = add_data;
    auth_data_ip.add_data_len = add_data_len;
    auth_data_ip.is_rsp = TRUE;
    auth_data_ip.ota_key = NULL;

    /* Generate Authentication Data and include it in the response.*/
    auth_data = googleFinder_GenerateAuthenticationData(instance, &auth_data_ip);

    if(auth_data != NULL)
    {
        memcpy(&rsp_data[GOOGLE_FINDER_BEACON_RSP_AUTH_OFFSET], auth_data, GOOGLE_FINDER_BEACON_RSP_AUTH_LEN);
        free(auth_data);
    }
    else
    {
        DEBUG_LOG("googleFinder_BeaconActionServiceResponse. Generated authentication data is invalid.");
    }

    if(add_data != NULL)
    {
        memcpy(&rsp_data[GOOGLE_FINDER_BEACON_RSP_ADD_DATA_OFFSET], add_data, add_data_len);
    }

    DEBUG_LOG_VERBOSE("googleFinder_BeaconActionServiceResponse: service response data:");
    DEBUG_LOG_DATA_VERBOSE(rsp_data, rsp_len);

    FastPair_BeaconServiceResponse(instance->conn_id, rsp_data, rsp_len);

    free(rsp_data);
}

/* \brief Function to get current clock value.
   \param void
   \return uint32 beacon time counter value
 */
static uint32 googleFinder_GetCurrentClockVal(void)
{
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();

    return google_finder_task_ptr->beacon_time_data.beacon_time;
}

/* \brief Function to get number of ringing components.
   \param void
   \return uint8 number of ringing components.
 */
static uint8 googleFinder_GetRingingComponents(void)
{
    uint8 provider_type = Multidevice_GetType();
    uint8 ring_component = GOOGLE_FINDER_NO_COMPONENT;

    if(provider_type == multidevice_type_single)
    {
        ring_component = GOOGLE_FINDER_SINGLE_COMPONENT;
    }
    else if (provider_type == multidevice_type_pair)
    {
        ring_component = GOOGLE_FINDER_TWO_COMPONENTS;
    }
    else
    {
        DEBUG_LOG("googleFinder_GetRingingComponents: Invalid provider type %d received", provider_type);
    }

    DEBUG_LOG("googleFinder_GetRingingComponents: Number of components capable of ringing = %d", ring_component);

    return ring_component;
}
/*! \brief Handler function to read Beacon Parameter.

    \param instance pointer to google finder connection instance 
    \return void
*/
static void googleFinder_HandleReadBeaconParam(google_finder_instance* instance)
{
    uint8* beacon_param = NULL;
    uint8* encrypted_add_data = NULL;
    uint32 beacon_time_counter = googleFinder_GetCurrentClockVal();


    beacon_param = (uint8*)PanicUnlessMalloc(GOOGLE_FINDER_BEACON_PARAM_RSP_ADD_DATA_LEN);
    encrypted_add_data = (uint8*)PanicUnlessMalloc(GOOGLE_FINDER_BEACON_PARAM_RSP_ADD_DATA_LEN);

    beacon_param[GOOGLE_FINDER_BEACON_PARAM_RSP_TX_POWER_OFFSET] = (uint8)TxPower_LEGetData();
    beacon_param[GOOGLE_FINDER_BEACON_PARAM_RSP_CLOCK_VALUE_OFFSET] = (uint8)((beacon_time_counter >> 24) & 0xff);
    beacon_param[GOOGLE_FINDER_BEACON_PARAM_RSP_CLOCK_VALUE_OFFSET + 1] = (uint8)((beacon_time_counter >> 16) & 0xff);
    beacon_param[GOOGLE_FINDER_BEACON_PARAM_RSP_CLOCK_VALUE_OFFSET + 2] = (uint8)((beacon_time_counter >> 8) & 0xff);
    beacon_param[GOOGLE_FINDER_BEACON_PARAM_RSP_CLOCK_VALUE_OFFSET + 3] = (uint8)(beacon_time_counter & 0xff);
    beacon_param[GOOGLE_FINDER_BEACON_PARAM_RSP_ENCRYPT_TYPE_OFFSET] = GOOGLE_FINDER_SECP160R1;
    beacon_param[GOOGLE_FINDER_BEACON_PARAM_RSP_RING_COMPONENTS_OFFSET] = googleFinder_GetRingingComponents();
    beacon_param[GOOGLE_FINDER_BEACON_PARAM_RSP_RING_CAPABILITY_OFFSET] = GOOGLE_FINDER_NO_RING_VOL_SELECTION;
    memset(&beacon_param[GOOGLE_FINDER_BEACON_PARAM_RSP_ZERO_PADDING_OFFSET], 0x00, GOOGLE_FINDER_ZERO_PADDING_LEN);

    DEBUG_LOG_V_VERBOSE("googleFinder_HandleReadBeaconParam: beacon param data:");
    DEBUG_LOG_DATA_V_VERBOSE(beacon_param, GOOGLE_FINDER_BEACON_PARAM_RSP_ADD_DATA_LEN);

    /* Beacon Paramter data should be AES-ECB-128 encrypted with the Account Key */
    aes_ecb_128_encrypt(beacon_param,
                        GOOGLE_FINDER_BEACON_PARAM_RSP_ADD_DATA_LEN,
                        encrypted_add_data,
                        instance->account_key);

    /* send beacon service response for read beacon parameter request */
    googleFinder_BeaconActionServiceResponse(instance,
                                             GOOGLE_FINDER_READ_BEACON_PARAMS,
                                             encrypted_add_data,
                                             GOOGLE_FINDER_BEACON_PARAM_RSP_ADD_DATA_LEN);

    free(beacon_param);
    free(encrypted_add_data);
}

/*! \brief Handler function to check if Provider is provisioned.
    \param  void
    \return bool TRUE if EIK is set, otherwise FALSE.
*/
static bool googleFinder_IsProvisioned(void)
{
    if(googleFinder_IsEIKZero())
    {
        return FALSE;
    }

    return TRUE;
}

/*! \brief Handler function to read Provisioning State.

    \param instance pointer to google finder connection instance 
    \return void
*/
static void googleFinder_HandleReadProvisionState(google_finder_instance* instance)
{
    uint8 *add_data;
    uint8* owner_account_key = NULL;
    uint16 add_data_len = GOOGLE_FINDER_PROV_STATE_RSP_ADD_DATA_LEN;

    add_data = (uint8*)PanicUnlessMalloc(GOOGLE_FINDER_PROV_STATE_RSP_ADD_DATA_WITH_P160_EID_LEN);
    memset(add_data, 0, GOOGLE_FINDER_PROV_STATE_RSP_ADD_DATA_WITH_P160_EID_LEN);

    owner_account_key = googleFinder_GetOwnerAccountKey();

    if (googleFinder_IsProvisioned())
    {
        add_data[GOOGLE_FINDER_PROV_STATE_RSP_STATE_OFFSET] |= GOOGLE_FINDER_PROV_STATE_RSP_EIK_SET_FLAG;
        add_data_len = GOOGLE_FINDER_PROV_STATE_RSP_ADD_DATA_WITH_P160_EID_LEN;

        if (google_finder_advert.adverts_enabled)
        {
            memcpy(&add_data[GOOGLE_FINDER_PROV_STATE_RSP_EID_OFFSET], 
                   &google_finder_adv_packet[GOOGLE_FINDER_ADV_EPHEMERAL_IDENTIFIER_OFFSET],
                   GOOGLE_FINDER_ADV_EPHEMERAL_IDENTIFIER_LEN);
        }
    }

    if (owner_account_key != NULL) 
    {
        if (memcmp(instance->account_key, owner_account_key, GOOGLE_FINDER_ACCOUNT_KEY_LEN) == 0)
        {
            add_data[GOOGLE_FINDER_PROV_STATE_RSP_STATE_OFFSET] |= GOOGLE_FINDER_PROV_STATE_RSP_OWNER_KEY_FLAG;
        }
    }

    DEBUG_LOG("googleFinder_HandleReadProvisionState resp");
    DEBUG_LOG_DATA_V_VERBOSE(add_data, add_data_len);

    googleFinder_BeaconActionServiceResponse(instance,
                                             GOOGLE_FINDER_READ_PROVISION_STATE,
                                             add_data,
                                             add_data_len);

    free(add_data);
    free(owner_account_key);
}

/*! \brief Handler function to store EIK in Persistent Store.
    \param uint8* pointer to EIK
    \return void
*/
static void googleFinder_StoreEIK(uint8* eik)
{
    device_t my_device = BtDevice_GetSelfDevice();
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();

    if (my_device != NULL)
    {
        google_finder_pddu_t google_finder_pddu = {0};

        google_finder_pddu.beacon_time_counter = google_finder_task_ptr->beacon_time_data.beacon_time;
        memcpy(google_finder_pddu.eik, eik, GOOGLE_FINDER_EIK_LEN);

        Device_SetProperty(my_device, device_property_google_finder, (void *)&google_finder_pddu, sizeof(google_finder_pddu_t));
        DeviceDbSerialiser_Serialise();

        DEBUG_LOG("googleFinder_StoreEIK: EIK written to PS successfully.");
    }
}

/*! \brief Verify hashed EIK in Set EIK message.

    \param eik_hash hashed EIK in set EIK message
    \param nonce one time random nonce corresponding to btConnId

    \return bool result of verification of hash operation
*/
static bool googleFinder_VerifyHashedEIK(uint8* eik_hash, uint8* nonce)
{
    uint8 *curr_eik = NULL;
    uint8 sha256_ip[GOOGLE_FINDER_EIK_LEN + GOOGLE_FINDER_ONE_TIME_NONCE_LEN];
    uint8 sha256_out[SHA256_DIGEST_SIZE];

    if ((curr_eik = googleFinder_GetEIK()) != NULL)
    {
        memcpy(&sha256_ip[0], curr_eik, GOOGLE_FINDER_EIK_LEN);
        memcpy(&sha256_ip[GOOGLE_FINDER_EIK_LEN], nonce, GOOGLE_FINDER_ONE_TIME_NONCE_LEN);
        sha256(sha256_ip, GOOGLE_FINDER_EIK_LEN + GOOGLE_FINDER_ONE_TIME_NONCE_LEN, sha256_out);
        free(curr_eik);

        /* verify if first 8 bytes of SHA256(Current EIK || the last nonce read) matches with hashed EIK. */
        if (memcmp(eik_hash, sha256_out, GOOGLE_FINDER_ONE_TIME_AUTH_KEY_LEN) == 0)
        {
            DEBUG_LOG("googleFinder_VerifyHashedEIK. Hashed EIK is matching with the first 8 octets of HMAC SHA256 output.");
            return TRUE;
        }
    }

    return FALSE;
}

/*! \brief Kick start the beacon time counter, time is updated every second.
    \param none
    \return none
*/
void googleFinder_KickBeaconTimeCounter(void)
{
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();
    bool is_provisioned = googleFinder_IsProvisioned();
    bool is_tracking_disabled = GoogleFinder_IsLocationTrackingDisabled();

    DEBUG_LOG("googleFinder_KickBeaconCounter is_provisioned %d is_tracking_disabled %d beacon_counter %d", 
                  is_provisioned, is_tracking_disabled,
                  google_finder_task_ptr->beacon_time_data.beacon_time);

    /* Start the second counter if location tracking is provisioned */
    if (is_provisioned)
    {
        google_finder_task_ptr->beacon_time_data.trigger_clock = VmGetClock();

        MessageCancelAll(&google_finder_task_ptr->task, 
                         GOOGLE_FINDER_INTERNAL_BEACON_TIME_UPDATE);
        MessageSendLater(&google_finder_task_ptr->task,
                         GOOGLE_FINDER_INTERNAL_BEACON_TIME_UPDATE,
                         NULL,
                         D_SEC(1));
    }
    else
    {
        MessageCancelAll(&google_finder_task_ptr->task, GOOGLE_FINDER_INTERNAL_BEACON_TIME_UPDATE);

        google_finder_task_ptr->beacon_time_data.beacon_time = 0;
        google_finder_task_ptr->beacon_time_data.drift_ms = 0;
    }
}

/*! \brief Handler function to set EIK.

    \param instance pointer to google finder connection instance 
    \param add_data additional data in set EIK message
    \param add_data_len additional data length

    \return bool result of processing of Set EIK message
*/
static bool googleFinder_HandleSetEIK(google_finder_instance* instance, uint8* add_data, uint8 add_data_len)
{
    bool is_decrypt_success = FALSE;
    uint8* eik;
    uint8* owner_account_key;
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();
    bool status = FALSE;
    bool isProvisioned = googleFinder_IsProvisioned();

    DEBUG_LOG("googleFinder_HandleSetEIK: additional data length = 0x%x, allowed = %d", add_data_len, google_finder_task_ptr->allow_provisioning);
 
    /* Reject set EIK procedure in following cases ,
     * provider currently is not provisioned and not in pairing mode (we shouldn't allow new provisioning) or 
     * additional data received is less than minimum expected length or
     * provider is already proviosined, but remote device didn't send data to prove the knowledge of current EIK
     */
    if((!isProvisioned && !google_finder_task_ptr->allow_provisioning) ||
       (add_data_len < GOOGLE_FINDER_SET_EIK_ADD_DATA_LEN_MIN) ||
       (isProvisioned && add_data_len != GOOGLE_FINDER_SET_EIK_WITH_HASH_ADD_DATA_LEN))
    {
        return FALSE;
    }

    /* Verify hashed EIK matches the currently provisioned EIK if the hashed EIK is included in additional data.*/
    if (add_data_len == GOOGLE_FINDER_SET_EIK_WITH_HASH_ADD_DATA_LEN)
    {
        if (!googleFinder_VerifyHashedEIK(&add_data[GOOGLE_FINDER_HASH_EIK_OFFSET], instance->random_nonce))
        {
            return FALSE;
        }

        instance->eik_updated = TRUE;
    }

    owner_account_key = googleFinder_GetOwnerAccountKey();

    if (owner_account_key == NULL)
    {
        return FALSE;
    }

    /* EIK which will be persisted in Provider */
    eik = (uint8*)PanicUnlessMalloc(GOOGLE_FINDER_EIK_LEN);


    /* Decrypt EIK using AES ECB 128 */
    is_decrypt_success = aes_ecb_128_decrypt(add_data,
                                             GOOGLE_FINDER_EIK_LEN,
                                             eik,
                                             owner_account_key);

    /* Persist EIK in PS */
    googleFinder_StoreEIK(eik);

    /* Provider is provisioned once EIK is persisted and decryption of EIK is successful.
       Do not allow provisioning after EIK is written to PS. */
    if (is_decrypt_success)
    {
        googleFinder_KickBeaconTimeCounter();
        /* Inform peer earbud to kick the beacon time counter */
        GoogleFinder_SyncBeaconTime();
        google_finder_task_ptr->allow_provisioning = FALSE;
        status = TRUE;

        /* Send service response data */
        googleFinder_BeaconActionServiceResponse(instance,
                                                 GOOGLE_FINDER_SET_EIK,
                                                 NULL,
                                                 0);
    }
    else
    {
        status =  FALSE;
    }

    free(eik);
    free(owner_account_key);

    return status;
}

/*! \brief Handler function to clear EIK.

    \param instance pointer to google finder connection instance 
    \param add_data additional data in Clear EIK message
    \param add_data_len additional data length

    \return bool result of verification of hash operation
*/
static bool googleFinder_HandleClearEIK(google_finder_instance* instance, uint8* add_data, uint8 add_data_len)
{
    device_t my_device = BtDevice_GetSelfDevice();

    DEBUG_LOG("googleFinder_HandleClearEIK: additional data length = 0x%x", add_data_len);

    /* If additional data received is less than minimum expected length or
       if Provider is not provisioned already, return FALSE.*/
    if((add_data_len < GOOGLE_FINDER_CLEAR_EIK_ADD_DATA_LEN) ||
        (!googleFinder_IsProvisioned()))
    {
        return FALSE;
    }

    /* verify hashed EIK data*/
    if(!googleFinder_VerifyHashedEIK(add_data, instance->random_nonce))
    {
        return FALSE;
    }

    /* Clear google finder pddu data from PS */
    if (my_device != NULL)
    {
        google_finder_pddu_t google_finder_pddu = {0};

        Device_SetProperty(my_device, device_property_google_finder, (void *)&google_finder_pddu, sizeof(google_finder_pddu_t));
        DeviceDbSerialiser_Serialise();
    }

    googleFinder_KickBeaconTimeCounter();

    /* Stop advertising Eddystone frame */
    googleFinderAdv_StopAdvertisement();

    /* Send beacon action service response */
    googleFinder_BeaconActionServiceResponse(instance,
                                             GOOGLE_FINDER_CLEAR_EIK,
                                             NULL,
                                             0);
    return TRUE;
}

/*! \brief Handler function to read EIK with user consent.

    \param instance pointer to google finder connection instance 
    \return google_finder_gatt_status_t GATT operation status
*/
static google_finder_gatt_status_t googleFinder_HandleReadEIKUserConsent(google_finder_instance* instance)
{
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();
    uint8 *curr_eik = NULL;

    /* If device is not in pairing mode, Provider should return a No User Consent error. */
    if(!google_finder_task_ptr->is_pairing_mode)
    {
        return google_finder_status_no_user_consent;
    }

    if ((curr_eik = googleFinder_GetEIK()) != NULL)
    {
        uint8* encrypted_eik = (uint8*)PanicUnlessMalloc(GOOGLE_FINDER_EIK_LEN);
        uint8* owner_account_key = googleFinder_GetOwnerAccountKey();

        aes_ecb_128_encrypt(curr_eik,
                            GOOGLE_FINDER_EIK_LEN,
                            encrypted_eik,
                            owner_account_key);

        googleFinder_BeaconActionServiceResponse(instance,
                                                 GOOGLE_FINDER_READ_EIK_USER_CONSENT,
                                                 encrypted_eik,
                                                 GOOGLE_FINDER_EIK_LEN);

        free(curr_eik);
        free(encrypted_eik);
        free(owner_account_key);

        return google_finder_status_success;
    }

    return google_finder_status_auth_error;
}

/* Helper function to derive the ringtone volume as per the ring volume data byte */
static ringtone_volume_t googleFinder_GetRingVolume(uint8 ring_volume)
{
    ringtone_volume_t ring_vol = ring_vol_low;

    DEBUG_LOG("googleFinder_GetRingVolume. Vol Level: %02x",ring_volume);

    switch(ring_volume)
    {
        case GOOGLE_FINDER_RING_DEVICE_VOL_LOW:
        {
            ring_vol = ring_vol_medium;
        }
        break;

        case GOOGLE_FINDER_RING_DEVICE_VOL_MEDIUM:
        {
            ring_vol = ring_vol_high;
        }
        break;

        case GOOGLE_FINDER_RING_DEVICE_VOL_DEFAULT:
        case GOOGLE_FINDER_RING_DEVICE_VOL_HIGH:
        {
            ring_vol = ring_vol_max;
        }
        break;

        default:
        {
            DEBUG_LOG("googleFinder_GetRingVolume. Ring Volume not supported. Ring from low volume");
            ring_vol = ring_vol_low;
        }
        break;
    }

    return ring_vol;
}

/* Reset the ring data structure */
static void googleFinder_ResetRingData(void)
{
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();

    DEBUG_LOG("googleFinder_ResetRingData");

    google_finder_task_ptr->ring_data.ring_state = google_finder_ringing_stopped_gatt_request;
    google_finder_task_ptr->ring_data.ring_component = GOOGLE_FINDER_STOP_RING;
    google_finder_task_ptr->ring_data.ring_timeout = GOOGLE_FINDER_RING_DURATION_INVALID;

    if(google_finder_task_ptr->ring_data.ring_nonce != NULL)
    {
        free(google_finder_task_ptr->ring_data.ring_nonce);
        google_finder_task_ptr->ring_data.ring_nonce = NULL;
    }

    google_finder_task_ptr->ring_data.instance = NULL;
}

/*! \brief Handler function for ring state change. (Callback function handler when ring/mute is performed successfully).

    \param ring_component Ring components
    \param ring_state Currently ringing state
    \param ring_timeout Ring Duration

    \return void
*/
static void googleFinder_HandleRingStateChange(uint8 ring_component, google_finder_ringing_state ring_state, uint16 ring_timeout)
{
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();
    uint8 *rsp_data;

    google_finder_task_ptr->ring_data.ring_component = ring_component;
    google_finder_task_ptr->ring_data.ring_timeout = ring_timeout * 10;
    google_finder_task_ptr->ring_data.ring_state = ring_state;

    if(google_finder_task_ptr->ring_data.instance == NULL)
    {
        DEBUG_LOG("googleFinder_HandleRingStateChange. Invalid Connection ID.");
        return;
    }

    rsp_data = PanicUnlessMalloc(GOOGLE_FINDER_RING_DEVICE_RSP_ADD_DATA_LEN);

    rsp_data[GOOGLE_FINDER_RING_DEVICE_RSP_STATE_OFFSET] = google_finder_task_ptr->ring_data.ring_state;
    rsp_data[GOOGLE_FINDER_RING_DEVICE_RSP_RING_COMPONENT_OFFSET] = google_finder_task_ptr->ring_data.ring_component;
    rsp_data[GOOGLE_FINDER_RING_DEVICE_RSP_RING_DURATION_OFFSET] = (uint8)((google_finder_task_ptr->ring_data.ring_timeout >> 8) & 0xFF);
    rsp_data[GOOGLE_FINDER_RING_DEVICE_RSP_RING_DURATION_OFFSET + 1] = (uint8)(google_finder_task_ptr->ring_data.ring_timeout & 0xFF);

    DEBUG_LOG("googleFinder_HandleRingStateChange.BT Conn ID : %02x Ring Response Data:%d", 
                                                google_finder_task_ptr->ring_data.instance->conn_id, ring_state);
    DEBUG_LOG_DATA_DEBUG(rsp_data, GOOGLE_FINDER_RING_DEVICE_RSP_ADD_DATA_LEN);

    googleFinder_BeaconActionServiceResponse(google_finder_task_ptr->ring_data.instance,
                                             GOOGLE_FINDER_RING_DEVICE,
                                             rsp_data,
                                             GOOGLE_FINDER_RING_DEVICE_RSP_ADD_DATA_LEN);

    /* Reset the entire ring data if the ringing has stopped, device failed to ring/mute or ringing state is invalid */
    if(ring_state != google_finder_ringing_started)
    {
        googleFinder_ResetRingData();
    }

    free(rsp_data);
}

/*! \brief Handler function to ring device.

    \param instance pointer to google finder connection instance 
    \param add_data additional data in Ring Device message
    \param add_data_len additional data length

    \return google_finder_gatt_status_t GATT operation status
*/
static google_finder_gatt_status_t googleFinder_HandleRingDevice(google_finder_instance* instance,
                                                                 uint8* add_data,
                                                                 uint8 add_data_len)
{
    if(add_data_len == GOOGLE_FINDER_RING_ADD_DATA_LEN)
    {
        uint8 ring_component;
        uint16 ring_timeout;
        uint8 ring_volume;
        googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();

        DEBUG_LOG("googleFinder_HandleRingDevice");

        ring_component = add_data[GOOGLE_FINDER_RING_DATA_RING_COMPONENT_OFFSET];
        ring_timeout = (uint16)(((add_data[GOOGLE_FINDER_RING_DATA_RING_TIMEOUT_OFFSET] << 8) & 0xFFFF) |
                                        (add_data[GOOGLE_FINDER_RING_DATA_RING_TIMEOUT_OFFSET + 1]));
        ring_volume = add_data[GOOGLE_FINDER_RING_DATA_RING_VOLUME_OFFSET];

        google_finder_task_ptr->ring_data.ring_component = ring_component;
        google_finder_task_ptr->ring_data.instance = instance;

        /* Handle multiple start request gracefully */
        if(google_finder_task_ptr->ring_data.ring_nonce != NULL)
        {
            free(google_finder_task_ptr->ring_data.ring_nonce);
        }

        /* Store this nonce, as the ring response for this request and subsequent ring response notificaton 
         * sent when ringing stops after timeout has to use same nonce for authentication key generation.
         */
        google_finder_task_ptr->ring_data.ring_nonce = PanicUnlessMalloc(GOOGLE_FINDER_ONE_TIME_NONCE_LEN);
        memcpy(google_finder_task_ptr->ring_data.ring_nonce, instance->random_nonce, GOOGLE_FINDER_ONE_TIME_NONCE_LEN);

        /* Ring timeout received in write request is in deciseconds, convert it into seconds */
        ring_timeout = ring_timeout/10;

        DEBUG_LOG("googleFinder_HandleRingDevice. Ring Component : %02x Ring Volume: %02x Ring Timeout: %02x",ring_component, ring_volume, ring_timeout);

        if(!FastPair_RingMuteDevice(FASTPAIR_SEEKER_INVALID, ring_component, googleFinder_GetRingVolume(ring_volume), ring_timeout))
        {
            google_finder_ringing_state ring_state = google_finder_ringing_failed_to_start_or_stop;
            googleFinder_HandleRingStateChange(ring_component, ring_state, ring_timeout);
        }

        return google_finder_status_success;
    }
    else
    {
        DEBUG_LOG("googleFinder_HandleRingDevice. Invalid additional data length.");

        return google_finder_status_invalid_value;
    }
}

/*! \brief Handler function to read Ringing state.

    \param instance pointer to google finder connection instance 
    \return void
*/
static void googleFinder_HandleReadRingState(google_finder_instance* instance)
{
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();
    uint8 *rsp_data = PanicUnlessMalloc(GOOGLE_FINDER_READ_RING_STATE_RSP_ADD_DATA_LEN);

    rsp_data[GOOGLE_FINDER_READ_RING_STATE_RSP_RING_COMPONENT_OFFSET] = google_finder_task_ptr->ring_data.ring_component;
    rsp_data[GOOGLE_FINDER_READ_RING_STATE_RSP_RING_DURATION_OFFSET] = (uint8)((google_finder_task_ptr->ring_data.ring_timeout >> 8)& 0xFF);
    rsp_data[GOOGLE_FINDER_READ_RING_STATE_RSP_RING_DURATION_OFFSET + 1] = (uint8)(google_finder_task_ptr->ring_data.ring_timeout & 0xFF);

    DEBUG_LOG("googleFinder_HandleReadRingState. Read Ring State Response Data:");
    DEBUG_LOG_DATA_DEBUG(rsp_data, GOOGLE_FINDER_READ_RING_STATE_RSP_ADD_DATA_LEN);

    googleFinder_BeaconActionServiceResponse(instance, GOOGLE_FINDER_READ_RING_STATE, 
                                             rsp_data, GOOGLE_FINDER_READ_RING_STATE_RSP_ADD_DATA_LEN);
    free(rsp_data);
}

/*! \brief Handler function to activate unwanted tracking protection mode.

    \param instance pointer to google finder connection instance 
    \return void
*/
static void googleFinder_HandleActivateUnwantedTracking(google_finder_instance* instance, uint8* data, uint8 data_len)
{
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();

    if(data_len > 0)
    {
        google_finder_task_ptr->control_flags_in_utp_mode = data[0];
    }

    if (GOOGLE_FINDER_FLAGS_IS_UNWANTED_TRACKING_PROTECTION_MODE_SET(google_finder_advert.raw_flags) || 
        !google_finder_advert.adverts_enabled)
    {
        DEBUG_LOG("googleFinder_HandleActivateUnwantedTracking, redundant action:");
    }
    else
    {
        GOOGLE_FINDER_FLAGS_SET_UNWANTED_TRACKING_PROTECTION_MODE(google_finder_advert.raw_flags);
        GoogleFinder_SyncUnwantedTracking(TRUE);

        /* Trigger the update */
        googleFinderAdv_UpdateHashedFlagsAdvData();

        DEBUG_LOG("googleFinder_HandleActivateUnwantedTracking, enabled:");
    }

    googleFinder_BeaconActionServiceResponse(instance,
                                             GOOGLE_FINDER_ACTIVATE_UNWANTED_TRACKING,
                                             NULL,
                                             0);

#ifdef FIND_MY_DEVICE_ENABLE_DULT
    /* Sets DULT into separate mode immediately, bypassing the 30 minute timer */
    Dult_IndicateSeparated(true);
#endif

}

/*! \brief Handler function to deactivate unwanted tracking protection mode.

    \param instance pointer to google finder connection instance 
    \param data additional data in deactivate unwanted tracking protection mode message
    \param data_len additional data length

    \return void
*/
static void googleFinder_HandleDeactivateUnwantedTracking(google_finder_instance* instance,
                                                          uint8* data,
                                                          uint8 data_len)
{
    UNUSED(data);
    UNUSED(data_len);

    if (!GOOGLE_FINDER_FLAGS_IS_UNWANTED_TRACKING_PROTECTION_MODE_SET(google_finder_advert.raw_flags) || 
        !google_finder_advert.adverts_enabled)
    {
        DEBUG_LOG("googleFinder_HandleDeactivateUnwantedTracking, redundant action:");
    }
    else
    {
        googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();

        DEBUG_LOG("googleFinder_HandleDeactivateUnwantedTracking, disabled:");

        GOOGLE_FINDER_FLAGS_RESET_UNWANTED_TRACKING_PROTECTION_MODE(google_finder_advert.raw_flags);
        GoogleFinder_SyncUnwantedTracking(FALSE);

        /* Trigger the update */
        googleFinderAdv_UpdateHashedFlagsAdvData();

        if(google_finder_task_ptr->control_flags_in_utp_mode != 0)
        {
            google_finder_task_ptr->control_flags_in_utp_mode = 0;
        }
    }

    googleFinder_BeaconActionServiceResponse(instance,
                                             GOOGLE_FINDER_DEACTIVATE_UNWANTED_TRACKING,
                                             NULL,
                                             0);

#ifdef FIND_MY_DEVICE_ENABLE_DULT
    Dult_IndicateNearOwner();
#endif
}

/*! \brief Parse the data contained in the Beacon Action Write Request.

    \param btConnId connection identifier of write request packet
    \param data beacon action write request data
    \param data_len beacon action write request data length

    \return google_finder_gatt_status_t gatt operation status
*/
static google_finder_gatt_status_t googleFinder_ParseBeaconWritePacket(google_finder_instance* instance, uint8* data, uint16 data_len)
{
    bool is_ota_key_valid = FALSE;
    uint8 add_data_len = 0;
    google_finder_auth_data_input auth_data_ip;
    uint8 data_id;
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();

    if(data == NULL)
    {
        DEBUG_LOG("googleFinder_ParseBeaconWritePacket: Received data is NULL.");
        return google_finder_status_invalid_value;
    }

    if(data_len < GOOGLE_FINDER_BEACON_WRITE_REQ_MIN_LEN)
    {
        DEBUG_LOG("googleFinder_ParseBeaconWritePacket: Invalid data received.");
        return google_finder_status_invalid_value;
    }

    data_id = data[GOOGLE_FINDER_BEACON_REQ_DATA_ID_OFFSET];
    DEBUG_LOG("googleFinder_ParseBeaconWritePacket: Received data id = 0x%x, data length = 0x%x",
               data_id, data[GOOGLE_FINDER_BEACON_REQ_DATA_LEN_OFFSET]);

    auth_data_ip.data_id = data_id;
    auth_data_ip.data_len = data[GOOGLE_FINDER_BEACON_REQ_DATA_LEN_OFFSET];
    auth_data_ip.add_data = &data[GOOGLE_FINDER_BEACON_REQ_ADD_DATA_OFFSET];
    /* Actual additional data length would be data length of the request minus OTA Key length. */
    auth_data_ip.add_data_len = add_data_len = data[GOOGLE_FINDER_BEACON_REQ_DATA_LEN_OFFSET] - GOOGLE_FINDER_ONE_TIME_AUTH_KEY_LEN;
    auth_data_ip.is_rsp = FALSE;
    auth_data_ip.ota_key = &data[GOOGLE_FINDER_BEACON_REQ_OTA_KEY_OFFSET];

    /* Skip OTA if UTP is enabled and skip ring authentication control flag is set */
    if(data_id == GOOGLE_FINDER_RING_DEVICE && GoogleFinder_IsUnwantedTrackingProtModeEnabled() &&
      ((google_finder_task_ptr->control_flags_in_utp_mode & GOOGLE_FINDER_UTP_CONTROL_FLAG_SKIP_RING_AUTH) == GOOGLE_FINDER_UTP_CONTROL_FLAG_SKIP_RING_AUTH))
    {
        is_ota_key_valid = TRUE;
        DEBUG_LOG("googleFinder_ParseBeaconWritePacket: Skipping Ring Authentication");
    }
    else
    {
        /* Before processing the additional data in the message, verify one time authentication key present in the message.*/
        is_ota_key_valid = googleFinder_VerifyOTAKey(instance,
                                                     &auth_data_ip);
    }

    if(!is_ota_key_valid)
    {
        DEBUG_LOG("googleFinder_ParseBeaconWritePacket: OTA key verification failed.");
        return google_finder_status_auth_error;
    }

    switch(data_id)
    {
        case GOOGLE_FINDER_READ_BEACON_PARAMS:
        {
            googleFinder_HandleReadBeaconParam(instance);
        }
        break;

        case GOOGLE_FINDER_READ_PROVISION_STATE:
        {
            googleFinder_HandleReadProvisionState(instance);
        }
        break;

        case GOOGLE_FINDER_SET_EIK:
        {
            if (!googleFinder_HandleSetEIK(instance,
                                           &data[GOOGLE_FINDER_BEACON_REQ_ADD_DATA_OFFSET],
                                           add_data_len))
            {
                return google_finder_status_auth_error;
            }
            
        }
        break;

        case GOOGLE_FINDER_CLEAR_EIK:
        {
            if(!googleFinder_HandleClearEIK(instance,
                                            &data[GOOGLE_FINDER_BEACON_REQ_ADD_DATA_OFFSET],
                                            add_data_len))
            {
                return google_finder_status_auth_error;
            }
        }
        break;

        case GOOGLE_FINDER_READ_EIK_USER_CONSENT:
        {
            return googleFinder_HandleReadEIKUserConsent(instance);
        }

        case GOOGLE_FINDER_RING_DEVICE:
        {
            return googleFinder_HandleRingDevice(instance,
                                                &data[GOOGLE_FINDER_BEACON_REQ_ADD_DATA_OFFSET],
                                                add_data_len);
        }

        case GOOGLE_FINDER_READ_RING_STATE:
        {
            googleFinder_HandleReadRingState(instance);
        }
        break;

        case GOOGLE_FINDER_ACTIVATE_UNWANTED_TRACKING:
        {
            googleFinder_HandleActivateUnwantedTracking(instance, &data[GOOGLE_FINDER_BEACON_REQ_ADD_DATA_OFFSET], add_data_len);
        }
        break;

        case GOOGLE_FINDER_DEACTIVATE_UNWANTED_TRACKING:
        {
            googleFinder_HandleDeactivateUnwantedTracking(instance,
                                                          &data[GOOGLE_FINDER_BEACON_REQ_ADD_DATA_OFFSET],
                                                          add_data_len);
        }
        break;

        default:
        {
            DEBUG_LOG("googleFinder_ParseBeaconWritePacket: Unsupported Beacon Action Message.");
        }
        break;
    }

    return google_finder_status_success;
}

static bool googleFinder_IsWriteBeaconActionToBeDeferred(uint8 data_id)
{
    uint8 invalid_eid[GOOGLE_FINDER_ADV_EPHEMERAL_IDENTIFIER_LEN] = {0};

    /* If EIK has been written, but EID calculation is still in progress, defer this signal until EID calculation is done */
    return (data_id == GOOGLE_FINDER_READ_PROVISION_STATE && googleFinder_IsProvisioned() && 
            memcmp(&google_finder_adv_packet[GOOGLE_FINDER_ADV_EPHEMERAL_IDENTIFIER_OFFSET], invalid_eid, GOOGLE_FINDER_ADV_EPHEMERAL_IDENTIFIER_LEN) == 0);
}

static bool googleFinder_DeferWriteBeaconActionInd(google_finder_instance *instance, 
                                                   GATT_FAST_PAIR_SERVER_WRITE_BEACON_ACTION_IND_T *write_ind)
{
    if(instance->deferred_ind_data != NULL)
    {
        return FALSE;
    }

    DEBUG_LOG("googleFinder_DeferWriteBeaconActionInd");

    /* Allocate the memory for deferred_ind_data and store write_ind into it */
    instance->deferred_ind_data = PanicUnlessMalloc(sizeof(GATT_FAST_PAIR_SERVER_WRITE_BEACON_ACTION_IND_T) + (sizeof(uint8)*write_ind->size_value) - 1);
    memcpy(instance->deferred_ind_data, write_ind, sizeof(GATT_FAST_PAIR_SERVER_WRITE_BEACON_ACTION_IND_T));
    memcpy(instance->deferred_ind_data->value, write_ind->value, write_ind->size_value);

    return TRUE;
}

static void googleFinder_HandleWriteBeaconActionInd(GATT_FAST_PAIR_SERVER_WRITE_BEACON_ACTION_IND_T *write_ind)
{
    google_finder_gatt_status_t gatt_status = google_finder_status_invalid_value;
    google_finder_instance *instance;

    DEBUG_LOG("googleFinder_HandleWriteBeaconActionInd, btConnId=0x%x, length=0x%x", write_ind->btConnId, write_ind->size_value);
    DEBUG_LOG_DATA_DEBUG(write_ind->value, write_ind->size_value);

    /* Before write operation, remote device shall read the beacon actions characteristics, 
     * this would have created the connection instance 
     */
    if((instance = googleFinder_GetConnectionInstance(write_ind->btConnId)) != NULL)
    {
        uint8 data_id = write_ind->value[GOOGLE_FINDER_BEACON_REQ_DATA_ID_OFFSET];

        if(googleFinder_IsWriteBeaconActionToBeDeferred(data_id))
        {
            if(!googleFinder_DeferWriteBeaconActionInd(instance, write_ind))
            {
                DEBUG_LOG("googleFinder_HandleWriteBeaconActionInd. Beacon Indication can't be deferred.");
            }
        }
        else
        {
            /* Parse beacon action write request data to identify Data ID, length, OTA key and additional data*/
            gatt_status = googleFinder_ParseBeaconWritePacket(instance,
                                                              &(write_ind->value[0]),
                                                              write_ind->size_value);

            /* Send Beacon Action Write Response with appropriate gatt status code for the current write request */
            FastPair_BeaconActionWriteResponse(write_ind->fast_pair_server,
                                               write_ind->btConnId,
                                               gatt_status);

            /* Reset the nonce associated with this btConnId as it's no longer needed for this write request */
            googleFinder_ResetNonceForConnection(write_ind->btConnId);
        }
    }
}

/*! \brief Handle indications received from GATT FP SERVER for Google Finder
 */
static void googleFinder_GATTServerMsgHandler(MessageId id, Message message)
{
    switch(id)
    {
        case GATT_FAST_PAIR_SERVER_READ_BEACON_ACTION_IND:
        {
            googleFinder_HandleReadBeaconActionInd((GATT_FAST_PAIR_SERVER_READ_BEACON_ACTION_IND_T *)message);
        }
        break;

        case GATT_FAST_PAIR_SERVER_WRITE_BEACON_ACTION_IND:
        {
            googleFinder_HandleWriteBeaconActionInd((GATT_FAST_PAIR_SERVER_WRITE_BEACON_ACTION_IND_T *)message);
        }
        break;

        case GATT_FAST_PAIR_SERVER_BEACON_ACTION_NOTIFICATION_CFM:
        {
            DEBUG_LOG("googleFinder_GATTServerMsgHandler. Beacon Action Notify Cfm");
        }
        break;

        default:
        {
            DEBUG_LOG("googleFinder_GATTServerMsgHandler. Un handled MESSAGE:0x%x", id);
        }
        break;
    }
}

static void googleFinder_GattConnect(gatt_cid_t cid)
{
    /* We choose not to do anything on GATT Connect */
    DEBUG_LOG("googleFinder_OnGattConnect. Conn ID : 0x%x", cid);
}

static void googleFinder_GattDisconnect(gatt_cid_t cid)
{
    google_finder_instance *instance = googleFinder_GetConnectionInstance(cid);

    DEBUG_LOG("googleFinder_GattDisconnect. Conn ID: 0x%x", cid);

    googleFinder_ResetConnectionInstance(cid);
    googleFinder_ResetRingOperationConnId(cid);

    if(instance != NULL)
    {
        /* If the disconnecting instance has been used to update the EIK, Refresh advertisement upon LE disconnection */
        if(instance->eik_updated)
        {
            instance->eik_updated = FALSE;

            /* 
             * This flag is used to generate a new eddystone frame upon next time counter update.
             * Gets reset after notifying the LEAM about the new EID data.
             */
            google_finder_advert.eik_updated_pending_eid_change =TRUE;

            DEBUG_LOG("googleFinder_GattDisconnect. Refresh EID data in advertisement.");
        }

        if(instance->deferred_ind_data != NULL)
        {
            free(instance->deferred_ind_data);
            instance->deferred_ind_data = NULL;
        }
    }
}

/*! \brief Handle beacon time counter update
    \param none
    \return none
*/
static void googleFinder_HandleBeaconTimeUpdate(void)
{
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();
    uint32 cur_clock = VmGetClock();
    uint32 expected_clock = (google_finder_task_ptr->beacon_time_data.trigger_clock + D_SEC(1));
    
    google_finder_task_ptr->beacon_time_data.beacon_time++;

    /* There could be some delay in handling second counter message, account for such delays */
    if (cur_clock > expected_clock)
    {
        uint32 drift_ms;
    
        drift_ms = cur_clock - expected_clock;
        drift_ms += google_finder_task_ptr->beacon_time_data.drift_ms;
    
        /* Update beacon counter interms of sec with sum of carried over drift and current drift in ms */
        google_finder_task_ptr->beacon_time_data.beacon_time += (drift_ms/D_SEC(1));

        if (drift_ms/D_SEC(1) >= 1)
        {
            DEBUG_LOG("googleFinder_HandleBeaconTimeUpdate increment time by %d due to drift",drift_ms/D_SEC(1));
        }

        google_finder_task_ptr->beacon_time_data.drift_ms = (drift_ms % D_SEC(1));
    }

    googleFinderAdv_KickAdvDataChange();
}

/*! \brief Handle Pairing activity message from Pairing module.
    \param message pairing active message
    \return void
*/
static void googleFinder_PairingActivity(PAIRING_ACTIVITY_T *message)
{
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();

    DEBUG_LOG("googleFinder_PairingActivity status=enum:pairingActivityStatus:%d",
                message->status);

    switch(message->status)
    {
        case pairingActivityInProgress:
        {
            google_finder_task_ptr->is_pairing_mode = TRUE;
            google_finder_task_ptr->allow_provisioning = TRUE;
            MessageCancelAll(&google_finder_task_ptr->task, GOOGLE_FINDER_INTERNAL_PROVISIONING_TIMEOUT);
        }
        break;

        case pairingActivitySuccess:
        case pairingActivityNotInProgress:
        {
            google_finder_task_ptr->is_pairing_mode = FALSE;

            if(google_finder_task_ptr->allow_provisioning)
            {
                MessageCancelAll(&google_finder_task_ptr->task, GOOGLE_FINDER_INTERNAL_PROVISIONING_TIMEOUT);
                MessageSendLater(&google_finder_task_ptr->task,
                                 GOOGLE_FINDER_INTERNAL_PROVISIONING_TIMEOUT,
                                 NULL,
                                 D_SEC(GOOGLE_FINDER_PROVISIONING_EXT_TIMEOUT));
            }
        }
        break;

        default:
            DEBUG_LOG("googleFinder_PairingActivity.  Invalid message id");
        break;
    }
}

/*! \brief Enable location tracking as requested by user.
           Start Google Finder advertisement if it was disabled and the device is provisioned.
 */
static void googleFinder_EnableLocationTracking(void)
{
    device_t my_device = BtDevice_GetSelfDevice();

    DEBUG_LOG("googleFinder_EnableLocationTracking");

    if(my_device == NULL)
    {
        DEBUG_LOG_ERROR("googleFinder_EnableLocationTracking. Self device is NULL");
        return;
    }

    Device_SetPropertyU8(my_device, device_property_google_finder_location_tracking_disabled, FALSE);
    DeviceDbSerialiser_Serialise();

    googleFinderAdv_StartAdvertisement();
}

/*! \brief Disable Location tracking as requested by user.
           Stop Google Finder advertisement if it is enabled.
 */
static void googleFinder_DisableLocationTracking(void)
{
    device_t my_device = BtDevice_GetSelfDevice();

    DEBUG_LOG("googleFinder_DisableLocationTracking");

    if(my_device == NULL)
    {
        DEBUG_LOG_ERROR("googleFinder_DisableLocationTracking. Self device is NULL");
        return;
    }

    Device_SetPropertyU8(my_device, device_property_google_finder_location_tracking_disabled, TRUE);
    DeviceDbSerialiser_Serialise();

    googleFinderAdv_StopAdvertisement();
}

/*! \brief Handle messages from other components
 */
static void googleFinder_HandleMessage(Task task, MessageId id, Message message)
{
    UNUSED(task);

    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();
    DEBUG_LOG("googleFinder_HandleMessage: message id, MSG:0x%x", id);

    switch (id)
    {
        case PAIRING_ACTIVITY:
        {
            googleFinder_PairingActivity((PAIRING_ACTIVITY_T*)message);
        }
        break;

        case GOOGLE_FINDER_INTERNAL_PROVISIONING_TIMEOUT:
        {
            DEBUG_LOG("GoogleFinder provisioning timeout");
            google_finder_task_ptr->allow_provisioning = FALSE;
        }
        break;

        case GOOGLE_FINDER_INTERNAL_BEACON_TIME_UPDATE:
        {
            googleFinder_HandleBeaconTimeUpdate();
            googleFinder_KickBeaconTimeCounter();
        }
        break;

        case ui_input_enable_google_finder_location_tracking:
            googleFinder_EnableLocationTracking();
            break;

        case ui_input_disable_google_finder_location_tracking:
            googleFinder_DisableLocationTracking();
            break;

        case ui_input_cancel_ring:
            FastPair_HandleLocalCancelRingDevice();
            break;

        case CL_CRYPTO_ENCRYPT_AES_ECB_256_CFM:
        case CL_CRYPTO_GENERATE_ECC_P160_PRIVATE_KEY_CFM:
        case CL_CRYPTO_GENERATE_ECC_P160_PUBLIC_KEY_CFM:
            googleFinderAdv_HandleCryptoMessage(id, message);
            break;

        case APP_POWER_SLEEP_PREPARE_IND:
        {
            DEBUG_LOG_INFO("googleFinder_HandleMessage APP_POWER_SLEEP_PREPARE_IND");

            if (google_finder_task_ptr->beacon_time_data.beacon_time != 0)
            {
                googleFinder_PersistBeaconTimeCounter(google_finder_task_ptr->beacon_time_data.beacon_time);
            }

            appPowerSleepPrepareResponse(&google_finder_task_ptr->task);
        }
        break;
        
        case APP_POWER_SHUTDOWN_PREPARE_IND:
        {
            DEBUG_LOG_INFO("googleFinder_HandleMessage APP_POWER_SHUTDOWN_PREPARE_IND");

            if (google_finder_task_ptr->beacon_time_data.beacon_time != 0)
            {
                googleFinder_PersistBeaconTimeCounter(google_finder_task_ptr->beacon_time_data.beacon_time);
            }

            appPowerShutdownPrepareResponse(&google_finder_task_ptr->task);
        }
        break;

        default:
            DEBUG_LOG("googleFinder_HandleMessage. Unhandled MessageID = %d", id);
        break;
    }
}

static void googleFinder_InitializeTaskData(void)
{
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();
    uint8 index;

    /* Array list to support the finder connections for upto max LE connection instances */
    for(index = 0; index < MAX_BLE_CONNECTIONS; index++)
    {
        google_finder_task_ptr->connection_instance[index].random_nonce = NULL;
        google_finder_task_ptr->connection_instance[index].account_key = NULL;
        google_finder_task_ptr->connection_instance[index].conn_id = GOOGLE_FINDER_INVALID_CID;
        google_finder_task_ptr->connection_instance[index].eik_updated = FALSE;
        google_finder_task_ptr->connection_instance[index].deferred_ind_data = NULL;
    }

    google_finder_task_ptr->allow_provisioning = FALSE;
    google_finder_task_ptr->is_pairing_mode = FALSE;
    google_finder_task_ptr->control_flags_in_utp_mode = 0;

    /* 
     *  Read provisioning state and second_counter from persistent store.
     *  Kick the beacon time counter update.
     */
    google_finder_task_ptr->beacon_time_data.drift_ms = 0;
    google_finder_task_ptr->beacon_time_data.beacon_time = googleFinder_GetBeaconTimeCounter();

    /* Initialize the ring device data structure */
    memset(&google_finder_task_ptr->ring_data, 0, sizeof(google_finder_task_ptr->ring_data));
}

/*! \brief Get persisted beacon time counter value stored in PS.
    \param none
    \return uint32 Beacon Time Counter value
*/
uint32 googleFinder_GetBeaconTimeCounter(void)
{
    device_t my_device = BtDevice_GetSelfDevice();

    if(my_device != NULL)
    {
        google_finder_pddu_t *google_finder_pddu = NULL;
        size_t google_finder_pddu_size = 0;

        if(Device_GetProperty(my_device, device_property_google_finder, (void *)&google_finder_pddu, &google_finder_pddu_size))
        {
            DEBUG_LOG("googleFinder_GetBeaconTimeCounter - %02x", google_finder_pddu->beacon_time_counter);
            return google_finder_pddu->beacon_time_counter;
        }
    }

    return 0;
}

/*! \brief Persist beacon time counter when device shuts down or goes to sleep
    \param void
    \return void
 */
void googleFinder_PersistBeaconTimeCounter(uint32 beacon_time_counter)
{
    device_t my_device = BtDevice_GetSelfDevice();

    if(my_device != NULL)
    {
        google_finder_pddu_t *google_finder_pddu = NULL;
        size_t google_finder_pddu_size = 0;

        google_finder_pddu_t new_google_finder_pddu = {0};

        if(Device_GetProperty(my_device, device_property_google_finder, (void *)&google_finder_pddu, &google_finder_pddu_size))
        {
            memcpy(new_google_finder_pddu.eik, google_finder_pddu->eik, GOOGLE_FINDER_EIK_LEN);
            new_google_finder_pddu.beacon_time_counter = beacon_time_counter;

            Device_SetProperty(my_device, device_property_google_finder, (void *)&new_google_finder_pddu, sizeof(google_finder_pddu_t));
            DeviceDbSerialiser_Serialise();
        }
    }
}

bool GoogleFinder_IsUnwantedTrackingProtModeEnabled(void)
{
    bool is_unwanted_tracking_prot_mode_set = FALSE;

    if (GOOGLE_FINDER_FLAGS_IS_UNWANTED_TRACKING_PROTECTION_MODE_SET(google_finder_advert.raw_flags))
    {
        is_unwanted_tracking_prot_mode_set = TRUE;
    }

    return is_unwanted_tracking_prot_mode_set;
}

bool GoogleFinder_IsLocationTrackingDisabled(void)
{
    device_t my_device = BtDevice_GetSelfDevice();
    uint8 location_tracking_disabled = 0;

    if(my_device != NULL)
    {
        Device_GetPropertyU8(my_device, device_property_google_finder_location_tracking_disabled, &location_tracking_disabled);
    }

    if(location_tracking_disabled)
    {
        return TRUE;
    }

    return FALSE;
}

void googleFinder_ProcessDeferredBeaconAction(void)
{
    uint8 index;
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();

    DEBUG_LOG("googleFinder_ProcessDeferredBeaconAction");

    for(index = 0; index < MAX_BLE_CONNECTIONS; index++)
    {
        if(google_finder_task_ptr->connection_instance[index].deferred_ind_data != NULL)
        {
            DEBUG_LOG("googleFinder_ProcessDeferredBeaconAction Index: %d", index);

            googleFinder_HandleWriteBeaconActionInd(google_finder_task_ptr->connection_instance[index].deferred_ind_data);

            /* Free the deferred indication data now */
            free(google_finder_task_ptr->connection_instance[index].deferred_ind_data);
            google_finder_task_ptr->connection_instance[index].deferred_ind_data = NULL;
        }
    }
}

void GoogleFinder_RegisterPersistentDeviceDataUser(void)
{
    DeviceDbSerialiser_RegisterPersistentDeviceDataUser(
        PDDU_ID_GOOGLE_FINDER,
        googleFinder_GetDeviceDataLen,
        googleFinder_SerialisePersistentDeviceData,
        googleFinder_DeserialisePersistentDeviceData);
}

bool GoogleFinder_Init(Task init_task)
{
    googleFinderTaskData *google_finder_task_ptr = googleFinder_GetTaskData();

    UNUSED(init_task);

    google_finder_task_ptr->task.handler = googleFinder_HandleMessage;

    DEBUG_LOG("GoogleFinder_Init");

    googleFinder_InitializeTaskData();

    /* Register with FP GFPS to receive Beacon Action GATT Indications */
    FastPair_RegisterGoogleFinderMessageHandler(googleFinder_GATTServerMsgHandler);

    /* Register with GATT Connect to receive connection notifications */
    GattConnect_RegisterObserver(&google_finder_gatt_connect_callback);

    /* Register with pairing module to know pairing activity status */
    Pairing_ActivityClientRegister(&(google_finder_task_ptr->task));
    /* Register with Fast Pair Message Stream with google finder callback 
       to get notified when ring device operation gets performed.
    */
    FastPair_RegisterRingDeviceCallback(googleFinder_HandleRingStateChange);

    /* If the device is provisioned for location tracking then we need to 
     * run timer to increment beacon time every second.
     */
    googleFinder_KickBeaconTimeCounter();

    /* Initialize Google Finder Advertising interface */
    googleFinderAdv_SetUpAdvertising();

#ifdef FIND_MY_DEVICE_ENABLE_DULT
    /* Initialize the DULT (Detect Unwanted Location Tracking) component */
    Dult_Init();
#endif

    if(!GoogleFinder_IsLocationTrackingDisabled() && googleFinder_IsProvisioned())
    {
        googleFinderAdv_StartAdvertisement();
    }
    else
    {
        googleFinderAdv_StopAdvertisement();
    }

    /* Register with UI component to get google finder related UI inputs */
    Ui_RegisterUiInputConsumer(&(google_finder_task_ptr->task), google_finder_ui_inputs, ARRAY_DIM(google_finder_ui_inputs));

    /* Initialization of Google Finder Sync component */
    googleFinder_SyncInit();

    /* Register for power management to receive sleep and shutdown events */
    appPowerClientRegister(&google_finder_task_ptr->task);
    appPowerClientAllowSleep(&google_finder_task_ptr->task);

    return TRUE;
}
#endif /* INCLUDE_GOOGLE_FINDER */
